﻿/*
 * RegExpress
 * 
 * Copyright (c) 2010, Daniel McGaughran
 * 
 * Licensed under the Apache Licence, Version 2.0 (the "Licence");
 * you may not use this file except in compliance with the Licence.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the Licence is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the Licence for the specific language governing permissions and
 * limitations under the Licence.
 * 
 */

using System;
using System.Windows.Controls;
using System.Windows;


namespace XrControls
{
	public class UniformWrapPanel : Panel
	{
		public static readonly DependencyProperty IsFullWidthProperty;

		private double _maxChildWidth;
		private double _maxChildHeight;
		private double _maxFullWidthChildWidth;
		private double _maxFullWidthChildHeight;
		private int _numFullWidthItems;
		private int _elementsPerRow;
		private Size _desiredSize;


		static UniformWrapPanel()
		{
			IsFullWidthProperty = DependencyProperty.RegisterAttached("IsFullWidth",
					typeof(bool), typeof(UniformWrapPanel), new FrameworkPropertyMetadata
					(false, FrameworkPropertyMetadataOptions.AffectsMeasure
					| FrameworkPropertyMetadataOptions.AffectsArrange));
		}



		public static void SetIsFullWidth(UIElement element, bool value)
		{
			element.SetValue(IsFullWidthProperty, value);
		}

		public static bool GetIsFullWidth(UIElement element)
		{
			return (bool)element.GetValue(IsFullWidthProperty);
		}



		protected override Size MeasureOverride(Size availableSize)
		{
			_numFullWidthItems = 0;

			_maxChildWidth = 0.0;
			_maxChildHeight = 0.0;
			_maxFullWidthChildWidth = 0.0;
			_maxFullWidthChildHeight = 0.0;
			_desiredSize = new Size();

			//Recursively measure children
			foreach (UIElement currentElement in base.InternalChildren)
			{
				currentElement.Measure(availableSize);

				if (GetIsFullWidth(currentElement))
				{
					_maxFullWidthChildWidth = Math.Max(_maxFullWidthChildWidth, currentElement.DesiredSize.Width);
					_maxFullWidthChildHeight = Math.Max(_maxFullWidthChildHeight, currentElement.DesiredSize.Height);
					_numFullWidthItems++;
				}
				else
				{
					_maxChildWidth = Math.Max(_maxChildWidth, currentElement.DesiredSize.Width);
					_maxChildHeight = Math.Max(_maxChildHeight, currentElement.DesiredSize.Height);
				}
			}

			_desiredSize = new Size(Math.Max(_maxFullWidthChildWidth, _maxChildWidth),
					_maxFullWidthChildHeight * _numFullWidthItems);

			//Ensure computed elements per row is at least 1; avoid div-by-zero.
			_elementsPerRow = (_maxChildWidth > 0.0) ? (int)(availableSize.Width / _maxChildWidth) : 0;
			_elementsPerRow = Math.Max(_elementsPerRow, 1);

			//Finalise desired width - defined solely by width of items that fit in the allocated space efficiently.
			_desiredSize.Width = Math.Max(_desiredSize.Width, _elementsPerRow * _maxChildWidth);

			int currentRunLength = 0;
			foreach (UIElement currentElement in base.InternalChildren)
			{
				if (!GetIsFullWidth(currentElement))
					currentRunLength++;
				else
				{
					//Compute size of blocks (ignoring full-width items - already counted)
					int numLines = (currentRunLength + _elementsPerRow - 1) / _elementsPerRow;
					_desiredSize.Height += numLines * _maxChildHeight;
					currentRunLength = 0;
				}

			}
			if (currentRunLength > 0)
			{
				//Compute size of remaining block
				int numLines = (currentRunLength + _elementsPerRow - 1) / _elementsPerRow;
				_desiredSize.Height += numLines * _maxChildHeight;
			}

			return _desiredSize;
		}



		protected override Size ArrangeOverride(Size finalSize)
		{
			Point currentPosition = new Point();

			if (_elementsPerRow > InternalChildren.Count)
				_elementsPerRow = Math.Max(1, InternalChildren.Count);
			double finalChildWidth = Math.Min(finalSize.Width / (double)_elementsPerRow, finalSize.Width);
			_maxFullWidthChildHeight = Math.Min(_maxFullWidthChildHeight, finalSize.Height);
			_maxChildHeight = Math.Min(_maxChildHeight, finalSize.Height);

			double previousHeight = 0.0;
			int elementsInCurrentRow = 1;
			bool isPreviousFullWidth = false;

			foreach (UIElement currentElement in base.InternalChildren)
			{
				Size childSize = new Size();
				bool isFullWidth = GetIsFullWidth(currentElement);
				if (isFullWidth)
				{
					childSize.Width = finalSize.Width;
					childSize.Height = _maxFullWidthChildHeight;
				}
				else
				{
					childSize.Width = finalChildWidth;
					childSize.Height = _maxChildHeight;
				}

				Rect childBounds = new Rect(currentPosition, childSize);
				currentElement.Arrange(childBounds);

				//Calculate new position
				if (isFullWidth || isPreviousFullWidth || elementsInCurrentRow >= _elementsPerRow)
				{
					//New row
					currentPosition.X = 0.0;
					currentPosition.Y += previousHeight;
					elementsInCurrentRow = 1;
				}
				else
				{
					//Same row
					currentPosition.X += childSize.Width;
					previousHeight = childSize.Height;
					elementsInCurrentRow++;
				}

				isPreviousFullWidth = isFullWidth;
			}

			//Keep panel occupying its allocated size.
			return finalSize;
		}
	

	}
}
